<script setup>
import { onMounted, reactive, ref } from 'vue';
import * as THREE from 'three';
import Stats from 'three/addons/libs/stats.module.js';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';

const modelRef = ref()
const emit = defineEmits(['mousedown'])

const title = reactive({
  value: '',
  x: 0,
  y:0
})

const pointer = ref(false)

let camera, scene, renderer, controls, stats;

let mesh;

const width = 1000
const height = 800

const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2( 1, 1 );

const color = new THREE.Color();
const white = new THREE.Color().setHex( 0xffffff );

function init() {

  camera = new THREE.PerspectiveCamera( 60, width / height, 0.1, 100 );
  camera.position.set( 2, 1.5, 9 );

  scene = new THREE.Scene();

  const light = new THREE.HemisphereLight( 0xffffff, 0x888888, 0.2 );
  light.position.set( 0, 1, 0 );
  scene.add( light );

  	const dirLight = new THREE.DirectionalLight( 0xffffff, 1 );
    dirLight.color.setHSL( 0.1, 1, 0.95 );
    dirLight.position.set( - 1, 1.75, 1 );
    dirLight.position.multiplyScalar( 30 );
    scene.add( dirLight );

    dirLight.castShadow = true;
    dirLight.shadow.mapSize.width = 2048;
    dirLight.shadow.mapSize.height = 2048;
    const d = 20;
    dirLight.shadow.camera.left = - d;
    dirLight.shadow.camera.right = d;
    dirLight.shadow.camera.top = d;
    dirLight.shadow.camera.bottom = - d;

    dirLight.shadow.camera.far = 3500;
    dirLight.shadow.bias = - 0.00005;

  renderer = new THREE.WebGLRenderer( { antialias: true } );
  renderer.setPixelRatio( window.devicePixelRatio );
  renderer.setSize(width, height );
  renderer.setAnimationLoop( animate );
  renderer.shadowMap.enabled = true;

  modelRef.value.appendChild( renderer.domElement );

  controls = new OrbitControls( camera, renderer.domElement );
  controls.enableDamping = true;
  controls.enableZoom = true;
  controls.enablePan = true;

  controls.target = new THREE.Vector3(0, 0, 4)

  window.addEventListener( 'resize', onWindowResize );
  document.addEventListener( 'mousemove', onMouseMove );
  document.addEventListener( 'mousedown', onMouseDown );
}

function onWindowResize() {

  camera.aspect = width / height;
  camera.updateProjectionMatrix();

  renderer.setSize( width, height );

}

const getRayTarget = () => {
  raycaster.setFromCamera( mouse, camera );

  const intersection = raycaster.intersectObject( scene );

  if ( intersection.length > 0 ) {
     return intersection[0].object
  }
}

function onMouseMove( event ) {

  event.preventDefault();

  mouse.x = ( event.offsetX / width ) * 2 - 1;
  mouse.y = - ( event.offsetY / height ) * 2 + 1;

  const obj = getRayTarget()
  if (obj?.parent?.isGroup) {
    title.value = obj.parent.name !== 'Scene' ? obj.parent.name : ''
    title.x = event.offsetX
    title.y = event.offsetY

    pointer.value = obj.parent.name == '摄像头'
  }
}

function onMouseDown(event) {
  event.preventDefault();

  mouse.x = ( event.offsetX / width ) * 2 - 1;
  mouse.y = - ( event.offsetY / height ) * 2 + 1;

  const obj = getRayTarget()
  if (obj) {
    if (obj.name == '摄像头' || obj?.parent.name == '摄像头') {
      emit('mousedown', obj)
    }
  }
}

function animate() {

  controls.update();

  renderer.render( scene, camera );
}

const loader = new GLTFLoader();

const loadModel = () => {
  loader.load(
  	'/办公室.glb',
  	function ( gltf ) {
      gltf.scene.traverse((obj) => {
        	obj.castShadow = true;
          obj.receiveShadow = true;
      })

      scene.add( gltf.scene );
  	}
  );
}

onMounted(() => {
  init()
  loadModel()
})
</script>

<template>
  <div ref="modelRef" class="model" :class="{ pointer: pointer }">
    <div v-if="title.value" :style="{ left: `${title.x}px`, top: `${title.y}px`}" class="title">{{ title.value }}</div>
  </div>
</template>

<style scoped>
  .model {
    position: relative;
  }

  .pointer {
    cursor: pointer;
  }

  .title {
    pointer-events: none;
    color: #000;
    background-color: rgb(255 255 255 / 0.5);
    padding: 5px 10px;
    border-radius: 5px;
    position: absolute;
  }
</style>
